package com.xhs.hawaii.purchase.service;

import com.xhs.hawaii.common.BusinessException;
import com.xhs.hawaii.common.PageQuery;
import com.xhs.hawaii.common.PageResult;
import com.xhs.hawaii.common.UserInfo;
import com.xhs.hawaii.common.log.TraceLogIdUtils;
import com.xhs.hawaii.common.utils.AssertUtils;
import com.xhs.hawaii.common.utils.DozerUtils;
import com.xhs.hawaii.common.utils.excel.MapUtils;
import com.xhs.hawaii.purchase.enums.ManualPoEvent;
import com.xhs.hawaii.purchase.enums.PauseEvent;
import com.xhs.hawaii.purchase.enums.PurchaseOrderStatusEnum;
import com.xhs.hawaii.purchase.enums.PurchaseOrderTypeEnum;
import com.xhs.hawaii.purchase.model.PurchaseOrder;
import com.xhs.hawaii.purchase.model.PurchaseOrderLog;
import com.xhs.hawaii.purchase.model.PurchaseOrderSku;
import com.xhs.hawaii.purchase.vo.BaseEventPoVo;
import com.xhs.hawaii.purchase.vo.ManualPoAddVo;
import com.xhs.hawaii.purchase.vo.ManualPoVo;
import com.xhs.hawaii.purchase.vo.PurchaseOrderVo;
import com.xhs.hawaii.purchase.vo.query.ManualPoQuery;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.support.TransactionTemplate;

import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @author tangchuang
 * @version 1.0
 * @since 2018/1/24
 */
@Component
@Slf4j
public class PurchaseOrderFacade {

    @Autowired
    private PurchaseOrderService purchaseOrderService;

    @Autowired
    private PurchaseOrderSkuService purchaseOrderSkuService;

    @Autowired
    private TransactionTemplate transactionTemplate;

    /**
     * @param purchaseId
     * @return
     */
    public List<PurchaseOrderLog> getLogsByPurchaseId(Long purchaseId) {
        return purchaseOrderService.getLogsByPurchaseId(purchaseId).stream().map(t -> {
            for (ManualPoEvent event : ManualPoEvent.values()) {
                if (event.name().equals(t)) {
                    t.setOperation(event.getDesc());
                    return t;
                }
            }
            for (PauseEvent pauseEvent : PauseEvent.values()) {
                if (pauseEvent.name().equals(t)) {
                    t.setOperation(pauseEvent.getDesc());
                    return t;
                }
            }
            return t;
        }).collect(Collectors.toList());
    }

    public void servicePause(BaseEventPoVo baseEventPoVo, UserInfo userInfo) {

        Long purchaseOrderId = baseEventPoVo.getPurchaseOrderId();
        AssertUtils.notNull(purchaseOrderId, "采购单id不能为空");
        PurchaseOrder purchaseOrder = purchaseOrderService.getById(purchaseOrderId);

        PurchaseOrderLog purchaseOrderLog = generatePurchaseOrderLog(baseEventPoVo, userInfo, purchaseOrder);

        PauseEvent pauseEvent = PauseEvent.valueOf(baseEventPoVo.getOperation());
        switch (pauseEvent) {
            case PAUSE:
            case AGREE_PAUSE:
                purchaseOrder.setPause(1);
                updatePoAndInsertLog(purchaseOrder, purchaseOrderLog);
                break;
            case REFUSE_PAUSE:
                purchaseOrder.setPause(0);
                updatePoAndInsertLog(purchaseOrder, purchaseOrderLog);
                break;
            default:
                throw new BusinessException("错误的pause operate");
        }
    }

    private PurchaseOrderLog generatePurchaseOrderLog(BaseEventPoVo baseEventPoVo, UserInfo userInfo, PurchaseOrder purchaseOrder) {
        PurchaseOrderLog purchaseOrderLog = new PurchaseOrderLog();
        purchaseOrderLog.setTraceLogId(TraceLogIdUtils.getTraceLogId());
        purchaseOrderLog.setPurchaseOrderId(purchaseOrder.getId());
        purchaseOrderLog.setPurchaseOrderNo(purchaseOrder.getPurchaseOrderNo());
        purchaseOrderLog.setOperation(baseEventPoVo.getOperation());
        purchaseOrderLog.setOperateTime(new Date());
        purchaseOrderLog.setOperatorId(userInfo.getId());
        purchaseOrderLog.setRemark(baseEventPoVo.getRemark());
        purchaseOrderLog.setOperatorName(userInfo.getName());
        return purchaseOrderLog;
    }

    /**
     * @param baseEventPoVo
     * @param userInfo
     */
    public void serviceManualPo(BaseEventPoVo baseEventPoVo, UserInfo userInfo) {
        PurchaseOrder purchaseOrder;
        if (baseEventPoVo instanceof ManualPoAddVo) {
            ManualPoAddVo manualPoAddVo = (ManualPoAddVo) baseEventPoVo;
            purchaseOrder = generateManualPurchaseOrder(manualPoAddVo, userInfo);
        } else {
            Long purchaseOrderId = baseEventPoVo.getPurchaseOrderId();
            AssertUtils.notNull(purchaseOrderId, "采购单id不能为空");
            purchaseOrder = purchaseOrderService.getById(purchaseOrderId);
        }

        PurchaseOrderLog purchaseOrderLog = generatePurchaseOrderLog(baseEventPoVo, userInfo, purchaseOrder);

        Assemble assemble = new Assemble().setBaseEventPoVo(baseEventPoVo).setUserInfo(userInfo).setPurchaseOrder(purchaseOrder).setPurchaseOrderLog(purchaseOrderLog);
        switch (PurchaseOrderStatusEnum.valueOf(purchaseOrder.getStatus())) {
            case WAITING_COMMIT:
                waitingCommitHandler(assemble);
                break;
            case WAITING_APPROVE:
                waitingApproveHandler(assemble);
                break;
            case REFUSED:
                refusedHandler(assemble);
                break;
            default:
                throw new BusinessException("no handle found");
        }
    }

    private PurchaseOrder generateManualPurchaseOrder(ManualPoAddVo manualPoAddVo, UserInfo userInfo) {
        PurchaseOrder purchaseOrder = new PurchaseOrder();
        purchaseOrder.setPurchaseOrderNo("");
        purchaseOrder.setPurchaseOrderType(PurchaseOrderTypeEnum.MANUAL.name());
        purchaseOrder.setStatus(PurchaseOrderStatusEnum.WAITING_COMMIT.name());
        purchaseOrder.setPayableAmount(manualPoAddVo.getPayableAmount());
        purchaseOrder.setCreateReason(manualPoAddVo.getCreateReason());
        purchaseOrder.setStage(0);
        purchaseOrder.setCreateTime(new Date());
        purchaseOrder.setCreatorId(userInfo.getId());
        purchaseOrder.setCreatorName(userInfo.getName());

        String originPurchaseOrderNo = manualPoAddVo.getOriginPurchaseOrderNo();
        PurchaseOrder originPurchaseOrder = purchaseOrderService.getByPurchaseOrderNo(originPurchaseOrderNo);
        AssertUtils.notNull(originPurchaseOrder, "原采购单不能为空,{}", originPurchaseOrderNo);

        copyOrigin2Manual(originPurchaseOrder, purchaseOrder);
        return purchaseOrder;
    }

    private void waitingCommitHandler(Assemble assemble) {
        ManualPoEvent event = ManualPoEvent.valueOf(assemble.getBaseEventPoVo().getOperation());
        PurchaseOrder purchaseOrder = assemble.getPurchaseOrder();
        PurchaseOrderLog purchaseOrderLog = assemble.getPurchaseOrderLog();
        switch (event) {
            case COMMIT:
            case SAVE:
                purchaseOrder.setStatus(PurchaseOrderStatusEnum.WAITING_APPROVE.name());
                if (purchaseOrder.getId() == null) {
                    insertPoAndInsertLog(purchaseOrder, purchaseOrderLog);
                } else {
                    updatePoAndInsertLog(purchaseOrder, purchaseOrderLog);
                }
            default:
                throw new BusinessException("采购单状态异常");
        }
    }

    private void waitingApproveHandler(Assemble assemble) {
        ManualPoEvent event = ManualPoEvent.valueOf(assemble.getBaseEventPoVo().getOperation());
        PurchaseOrder purchaseOrder = assemble.getPurchaseOrder();
        switch (event) {
            case AGREE:
                purchaseOrder.setStatus(PurchaseOrderStatusEnum.AGREED.name());
                updatePoAndInsertLog(purchaseOrder, assemble.getPurchaseOrderLog());
                break;
            case REFUSE:
                purchaseOrder.setStatus(PurchaseOrderStatusEnum.REFUSED.name());
                updatePoAndInsertLog(purchaseOrder, assemble.getPurchaseOrderLog());
                break;
            default:
                throw new BusinessException("采购单状态异常");
        }
    }

    private void refusedHandler(Assemble assemble) {
        ManualPoEvent event = ManualPoEvent.valueOf(assemble.getBaseEventPoVo().getOperation());
        PurchaseOrder purchaseOrder = assemble.getPurchaseOrder();
        switch (event) {
            case COMMIT:
            case SAVE:
                purchaseOrder.setStatus(PurchaseOrderStatusEnum.WAITING_APPROVE.name());
                updatePoAndInsertLog(purchaseOrder, assemble.getPurchaseOrderLog());
                break;
            default:
                throw new BusinessException("采购单状态异常");
        }
    }

    public void insertPoAndInsertLog(PurchaseOrder purchaseOrder, PurchaseOrderLog purchaseOrderLog) {
        transactionTemplate.execute(t -> {
            purchaseOrderService.insert(purchaseOrder);
            purchaseOrderLog.setPurchaseOrderId(purchaseOrder.getId());
            purchaseOrderService.insertLog(purchaseOrderLog);
            return 1;
        });
    }

    public void updatePoAndInsertLog(PurchaseOrder purchaseOrder, PurchaseOrderLog purchaseOrderLog) {
        transactionTemplate.execute(t -> {
            int result = purchaseOrderService.update(purchaseOrder);
            if (result == 0) {
                throw new BusinessException("数据并发修改，请重试");
            }
            purchaseOrderService.insertLog(purchaseOrderLog);
            return 1;
        });
    }

    private void copyOrigin2Manual(PurchaseOrder originPurchaseOrder, PurchaseOrder purchaseOrder) {
        purchaseOrder.setCurrency(originPurchaseOrder.getCurrency());
        purchaseOrder.setExchangeRate(originPurchaseOrder.getExchangeRate());
        purchaseOrder.setProviderCode(originPurchaseOrder.getProviderCode());
        purchaseOrder.setLogistic(originPurchaseOrder.getLogistic());
        purchaseOrder.setPurchaseNum(originPurchaseOrder.getPurchaseNum());
        purchaseOrder.setOriginPurchaseOrderNo(originPurchaseOrder.getPurchaseOrderNo());
        purchaseOrder.setPurchaseAmount(originPurchaseOrder.getPurchaseAmount());
        purchaseOrder.setPurchaseAgent(originPurchaseOrder.getPurchaseAgent());
    }

    public PurchaseOrderVo getDetail(Long purchaseOrderId) {

        PurchaseOrder purchaseOrder = purchaseOrderService.getById(purchaseOrderId);

        List<PurchaseOrderSku> purchaseOrderSkuList = purchaseOrderSkuService.getByPurchaseOrderId(purchaseOrderId);

        return new PurchaseOrderVo();
    }

    public PageResult<ManualPoVo> queryManualPoVo(ManualPoQuery manualPoQuery) {
        Map<String, Object> params = manualQuery2Map(manualPoQuery);

        int count = purchaseOrderService.getManualPoCount(params);
        addPageKV(params, manualPoQuery);
        List<PurchaseOrder> manualPoList = purchaseOrderService.getManualPoList(params);

        List<ManualPoVo> manualPoVoList = DozerUtils.transferList(manualPoList, ManualPoVo.class);
        return PageResult.of(manualPoQuery, count, manualPoVoList);
    }

    private Map<String, Object> manualQuery2Map(ManualPoQuery manualPoQuery) {
        MapUtils.Builder<String, Object> builder = MapUtils.builder();
        return builder
                .put("creatorId", manualPoQuery.getCreatorId())
                .put("startTime", manualPoQuery.getStartTime())
                .put("endTime", manualPoQuery.getEndTime())
                .put("purchaseOrderNo", manualPoQuery.getPurchaseOrderNo())
                .put("status", manualPoQuery.getStatus())
                .put("purchaseAgent", manualPoQuery.getPurchaseAgent())
                .put("originPurchaseOrderNo", manualPoQuery.getOriginPurchaseOrderNo())
                .put("providerCode", manualPoQuery.getProviderCode())
                .build();
    }

    private void addPageKV(Map<String, Object> params, PageQuery pageQuery) {
        MapUtils.builder(params)
                .put("start", pageQuery.getStart())
                .put("pageSize", pageQuery.getPageSize());
    }

}
